#include <stdio.h>
#include <stdlib.h>


/* ----------------------------------- */
/*     Definition du type pixel        */
/* ----------------------------------- */


struct sPixel
{
  unsigned char red;
  unsigned char green;
  unsigned char blue;
};
typedef struct sPixel pixel;




/* -------------------------------------------------------------- */
/*     Declarations des procedures que vous pouvez appeler        */
/* (deja ecrites, code disponible pour information apres le main) */
/* -------------------------------------------------------------- */


/* Precondition : nomFichier est le nom d'un fichier au format Bitmap, 
   24-bit par pixel (donc pas de palette), non compresse. 
   Postconditions : largeur et hauteur contiennent la largeur (nombre de colonnes) 
   et la hauteur (nombre de lignes) de l'image, en nombre de pixels. 
*/
void lireEntete(const char nomFichier[], unsigned int * largeur, unsigned int * hauteur);



/* Preconditions : 
   Postconditions : un nouveau fichier appele comme la chaine contenue dans NomFic
   est cree. Son contenu est l'entete d'un fichier BMP (24-bit par pixel, non compresse)
   pour une image de taille largeur x hauteur. */
void ecrireEntete(const char nomFic[], unsigned int largeur, unsigned int hauteur);


/* Preconditions : tab est un tableau 1D de pixels, suffisamment grand pour contenir 
   tous les pixels de l'image. nomFichier est le nom d'un fichier au format Bitmap, 
   24-bit par pixel (donc pas de palette), non compresse. largeur et hauteur 
   sont les dimensions de l'image en nombre de pixels.
   Postcondition : Chaque case de tab correspond a un pixel de l'image et contient
   ainsi les valeurs RGB de ce pixel. Les premieres cases de tab contiennent la ligne 
   de pixels tout en bas de l'image. Les cases suivantes contiennent les pixels de la 
   ligne juste au-dessus (2e ligne en partant du bas), etc, jusqu'a la ligne du haut 
   de l'image.
*/
void remplirTableauPixelsDepuisFichier(const char nomFichier[], pixel tab[], \
                                       unsigned int largeur, unsigned int hauteur);




/* Preconditions : tab est un tableau contenant largeur*hauteur pixels. nomFic contient
   deja l'entete du fichier (format bitmap 24-bit par pixel, non compresse).
   Postconditions : nomFic est complete avec les valeurs RBG des pixels.
 */
void ecrirePixelsDansFichier(const char nomFic[], const pixel tab[], \
                             unsigned int largeur, unsigned int hauteur);




/* Precondition: 0 <= numbit <= 7, 0 correspond au bit de poids faible
   Resultat: on recupere le bit numero 'numbit' de 'nombre', c'est-a-dire
   0 ou 1 stocke dans un unsigned char. 
*/
unsigned char getIemeBit(unsigned char nombre, unsigned char numbit);




/* Precondition: 0 <= numbit <= 7, 0 correspond au bit de poids faible
   Postcondition: le bit numero 'numbit' de 'nombre' est mis a 0
*/
void setIemeBit0(unsigned char * nombre, unsigned char numbit);




/* Precondition: 0 <= numbit <= 7, 0 correspond au bit de poids faible
   Postcondition: le bit numero 'numbit' de 'nombre' est mis a 1
*/
void setIemeBit1(unsigned char * nombre, unsigned char numbit);






/* -------------------------------------------------------------- */
/*     Ecrivez ci-dessous les codes des procedures demandees      */
/* -------------------------------------------------------------- */


void traitementNiveauxDeGris (const pixel tab[], pixel NDG[], unsigned int largeur, unsigned int hauteur)
{	
	unsigned long ligne, colonne,indexe;
	unsigned int moy;
	for(ligne=0;ligne<hauteur;ligne++)
	{
		for(colonne=0;colonne<largeur;colonne++)
		{
			indexe=(ligne*largeur)+colonne;
			moy = (tab[indexe].red+tab[indexe].green+tab[indexe].blue)/3;
			NDG[indexe].red=moy;
			NDG[indexe].green=moy;
			NDG[indexe].blue=moy;
		}
	}
}





/* -------------------------- */
/*     Completez le main      */
/* -------------------------- */


int main()
{ 
	
	unsigned int largeur = 0;
 	unsigned int hauteur = 0;
	char nomFichier[] = "grey_wolf.bmp";
	int nbpixel;
	pixel * tab;
	pixel * tabndg;
	pixel * tabembossee;
  /* Lire l'entete du fichier image original pour recuperer la largeur et la hauteur (en nb de pixels) */

	lireEntete(nomFichier,&largeur,&hauteur);
	printf("Longueur = %u et Hauteur = %u \n", largeur, hauteur);
	nbpixel = largeur*hauteur;

  /* Reserver un tableau de pixels suffisamment grand pour contenir les valeurs RGB des pixels */
 	tab = (pixel *)malloc(nbpixel*sizeof(pixel));
  /* Remplir le tableau depuis le fichier */
 
	remplirTableauPixelsDepuisFichier(nomFichier,tab,largeur,hauteur);


  /* Reserver un second tableau de pixels pour la version niveaux de gris de l'image */

	tabndg = (pixel *)malloc(nbpixel*sizeof(pixel));

  /* Appeler la procedure qui calcule les pixels de la version niveaux de gris */

  	traitementNiveauxDeGris (tab, tabndg, largeur,hauteur);
	ecrirePixelsDansFichier("grey_wolf.bmp", tabndg, largeur, hauteur);
	
  /* Ecrire l'entete et les pixels de la version niveau de gris dans un nouveau fichier */
	
	ecrireEntete("FicNDG.bmp",largeur,hauteur);

  /* Reserver un second tableau de pixels pour la version embossee de l'image */
	
	tabembossee = (pixel *)malloc(nbpixel*sizeof(pixel));
	
  /* Appeler la procedure qui calcule les pixels de la version embossee */
	
	ecrirePixelsDansFichier(nomFichier, tabembossee, largeur, hauteur);
  
  /* Ecrire l'entete et les pixels de la version embossee dans un nouveau fichier */
  
	ecrireEntete("FicEmb.bmp",largeur,hauteur);

  /* Liberer la memoire allouee dynamiquement */
	
	free(tab);
	free(tabndg);
	free(tabembossee);

  return 0;

}








/* Procedure auxiliaire utile pour lire les fichiers */
void fskip(FILE *fp, int num_bytes)
{
  int i;
  for (i=0; i<num_bytes; i++) {fgetc(fp);}
}




void lireEntete(const char nomFichier[], unsigned int * largeur, unsigned int * hauteur)
{
  
  FILE * fic = fopen(nomFichier, "rb");
  unsigned short bitsparpixel = 0;
  unsigned int compression = 0, nbcolorsinpalette = 0, nbimportantcolors = 0;

  if (fgetc(fic)!='B' || fgetc(fic)!='M')
  {
    fclose(fic);
    fprintf(stderr, "%s n'est pas au format BMP.\n",nomFichier);
    exit(EXIT_FAILURE);
  }

  fskip(fic,16);
  fread(largeur, 4, 1, fic);
  fread(hauteur, 4, 1, fic);

  fskip(fic, 2); /* skipping the number of color planes (always 1) */
  fread(&bitsparpixel, 2, 1, fic);

  if(bitsparpixel != 24) 
    {
      fclose(fic);      
      fprintf(stderr, "Erreur: le nombre de bits par pixel n'est pas 24, \n");
      fprintf(stderr, "ce format d'image n'est pas supporte par ce programme. \n");
      exit(EXIT_FAILURE);
    }
  fread(&compression, 4, 1, fic);
  if(compression != 0) 
    {
      fclose(fic);
      fprintf(stderr, "Erreur: le mode de compression n'est pas 0, \n");
      fprintf(stderr, "ce format d'image n'est pas supporte par ce programme. \n");
      exit(EXIT_FAILURE);
    }

  fskip(fic,12);
  fread(&nbcolorsinpalette,4, 1, fic);
  if(nbcolorsinpalette != 0) 
    {
      fclose(fic);
      fprintf(stderr, "Erreur: le nombre de couleurs dans la palette n'est pas 0, \n");
      fprintf(stderr, "ce format d'image n'est pas supporte par ce programme. \n");
      exit(EXIT_FAILURE);
    }

  fread(&nbimportantcolors,4, 1, fic);
  if(nbimportantcolors != 0) 
    {
      fclose(fic);
      fprintf(stderr, "Erreur: le nombre de couleurs importantes n'est pas 0, \n");
      fprintf(stderr, "ce format d'image n'est pas supporte par ce programme. \n");
      exit(EXIT_FAILURE);
    }


  fclose(fic);
}





void remplirTableauPixelsDepuisFichier(const char nomFichier[], pixel tab[], \
                                       unsigned int largeur, unsigned int hauteur)
{
  FILE * fic = fopen(nomFichier, "rb");
  unsigned int ligne, colonne, padding;

  fskip(fic, 54);
  for(ligne = 0; ligne < hauteur; ligne ++)
    {
      for (colonne = 0; colonne < largeur; colonne ++)
        {
          tab[(ligne*largeur) + colonne].red = fgetc(fic);
          tab[(ligne*largeur) + colonne].green = fgetc(fic);
          tab[(ligne*largeur) + colonne].blue = fgetc(fic);
        }

      /* Padding for 4 byte alignment */
      if( (3*largeur)%4 == 0) padding = 0;
      else padding = 4 - ((3*largeur)%4);
      fskip(fic, padding);
    }

  fclose(fic);
}




void ecrireEntete(const char nomFic[], unsigned int largeur, unsigned int hauteur)
{
  FILE * fic = fopen(nomFic, "wb");

  unsigned int monInt;
  unsigned short monShort;
  unsigned int padding;
 
  char format[] = {'B', 'M'};
  unsigned int tailleFic = 54 + largeur*hauteur*3;

  
  fwrite(format, 1, 2, fic);

  /* size of the file in bytes */
  fwrite(&tailleFic, 4, 1, fic);

  /* Unused, app specific */
  monShort = 0;
  fwrite(&monShort, 2, 1, fic);
  fwrite(&monShort, 2, 1, fic);

  /* Offset for pixel data */
  monInt = 54;
  fwrite(&monInt, 4, 1, fic);

  /* Nb of bytes in the header from this point */
  monInt = 40;
  fwrite(&monInt, 4, 1, fic);

  fwrite(&largeur, 4, 1, fic);
  fwrite(&hauteur, 4, 1, fic);

  /* Nb of color planes (always 1) */
  monShort = 1;
  fwrite(&monShort, 2, 1, fic);

  /* Nb of bits per pixel */
  monShort = 24;
  fwrite(&monShort, 2, 1, fic);
  
  /* Compression mode */
  monInt = 0;
  fwrite(&monInt, 4, 1, fic);


  /* Size of the raw BMP data (after this header), including padding */
  if( (3*largeur)%4 == 0) padding = 0;
  else padding = 4 - ((3*largeur)%4);
  monInt = (largeur*3 + padding)*hauteur;
  fwrite(&monInt, 4, 1, fic);


  /* Horiz and vertic resolutions (pixel per metre) */
  monInt = 2835;
  fwrite(&monInt, 4, 1, fic);
  fwrite(&monInt, 4, 1, fic);


  /* Numbers of colors in the palette & number of important colors */ 
  monInt = 0;
  fwrite(&monInt, 4, 1, fic);
  fwrite(&monInt, 4, 1, fic);

  fclose(fic);

}



void ecrirePixelsDansFichier(const char nomFic[], const pixel tab[], \
                             unsigned int largeur, unsigned int hauteur)
{
  FILE * fic = fopen(nomFic, "ab");
  unsigned char monChar, i;
  unsigned int padding, ligne, colonne, index;

  /* Pixel data */
  
  for(ligne = 0; ligne < hauteur; ligne ++)
    {
      for (colonne = 0; colonne < largeur; colonne ++)
        {
          index = largeur*ligne + colonne;
          fwrite(&tab[index].red, 1, 1, fic);
          fwrite(&tab[index].green, 1, 1, fic);
          fwrite(&tab[index].blue, 1, 1, fic);
        }

      /* Padding for 4 byte alignment */
      if( (3*largeur)%4 == 0) padding = 0;
      else padding = 4 - ((3*largeur)%4);
      monChar = 0;
      for (i = 0; i < padding; i++) fwrite(&monChar, 1, 1, fic);
    }

  fclose(fic);
}





unsigned char getIemeBit(unsigned char nombre, unsigned char numbit)
{
  return (nombre & (1 << numbit)) >> numbit;
}




void setIemeBit0(unsigned char * nombre, unsigned char numbit)
{
  *nombre = (*nombre) & (~(1 << numbit));
}



void setIemeBit1(unsigned char * nombre, unsigned char numbit)
{
  *nombre = (*nombre) | (1 << numbit);
}
